/**
 * 
 */
package com.patternity.model;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Stores every design declaration
 * 
 * @author cyrillemartraire
 */
// @Repository(entity = DesignDeclaration.class)
public class AllDeclarations {

	/**
	 * Stores every design declaration built from scanning the annotations
	 */
	private final Collection<DesignDeclaration> declarations = new ArrayList<DesignDeclaration>();

	private final Map<String, Set<DesignDeclaration>> byElementName = new HashMap<String, Set<DesignDeclaration>>();

	public void addDeclaration(final DesignDeclaration dd) {
		Set<DesignDeclaration> set = byElementName.get(dd.getSite());
		if (set == null) {
			set = new HashSet<DesignDeclaration>();
			byElementName.put(dd.getSite(), set);
		}
		if (set.add(dd)) {
			declarations.add(dd);
		}
	}

	public Set<DesignDeclaration> findByElementName(final String name) {
		final Set<DesignDeclaration> set = byElementName.get(name);
		return set == null ? Collections.<DesignDeclaration> emptySet() : set;
	}

	public List<DesignDeclaration> allDeclarations() {
		return new ArrayList<DesignDeclaration>(declarations);
	}

	/**
	 * Derives declarations implied from previous declarations, e.g. the
	 * Quantity pattern implies the Value Object pattern, Value Object implies
	 * Immutable, and a Layer declaration on a package implies every element
	 * contained also is part of the layer.
	 */
	public void deriveImplied(final Catalog catalog, final ModelBuilder given) {
		for (final DesignDeclaration dd : allDeclarations()) {
			final Pattern pattern = catalog.get(dd.getName());
			deriveImplied(dd, pattern, given);
		}

	}

	protected void deriveImplied(final DesignDeclaration dd,
			final Pattern pattern, final ModelBuilder given) {
		if (pattern == null) {
			return;
		}
		for (final String implied : pattern.getImpliedPatterns()) {
			// TODO keep traceability of the DD it comes from
			addDeclaration(new DesignDeclaration(dd.getSite(), implied));
		}
		if (pattern.isModuleWise()) {
			for (final String member : members(dd.getSite(), given.elements())) {
				addDeclaration(new DesignDeclaration(member, pattern.getName()));
			}
		}
	}

	private final static Iterable<String> members(final String site,
			final Iterable<Element> elements) {
		final List<String> members = new ArrayList<String>();
		for (final Element element : elements) {
			final String qName = element.getName();
			if (qName.startsWith(site)) {
				members.add(qName);
			}
		}
		return members;
	}

}
